package ink.fujisann.learning.stock.controller;


import com.github.xiaoymin.knife4j.annotations.ApiSort;
import ink.fujisann.learning.base.SortConst;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Swagger;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponents;
import springfox.documentation.service.Documentation;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.springframework.util.StringUtils.isEmpty;
import static springfox.documentation.spring.web.paths.Paths.splitCamelCase;
import static springfox.documentation.swagger.common.HostNameProvider.componentsFrom;

@ApiSort(SortConst.coinSort)
@Api(tags = {"加密货币"})
@RestController
@RequestMapping("/coin")
public class CoinController {

    private DocumentationCache documentationCache;

    @Autowired
    public void setDocumentationCache(DocumentationCache documentationCache) {
        this.documentationCache = documentationCache;
    }

    private ServiceModelToSwagger2Mapper mapper;

    @Autowired
    public void setMapper(ServiceModelToSwagger2Mapper mapper) {
        this.mapper = mapper;
    }

    private JsonSerializer jsonSerializer;

    @Autowired
    public void setJsonSerializer(JsonSerializer jsonSerializer) {
        this.jsonSerializer = jsonSerializer;
    }

    private ApplicationContext applicationContext;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    private Environment environment;

    @Autowired
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @ApiOperation("插入加密货币基础数据")
    @GetMapping("/insertCoinBase")
    public Json insertCoinBase(HttpServletRequest servletRequest) {
        // 生成Swagger对象
        String groupName = "default";
        Documentation documentation = documentationCache.documentationByGroup(groupName);
        Swagger swagger = mapper.mapDocumentation(documentation);
        UriComponents uriComponents = componentsFrom(servletRequest, swagger.getBasePath());
        swagger.basePath(isEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
        if (isEmpty(swagger.getHost())) {
            swagger.host(hostName(uriComponents));
        }

        // 中文描述:类名
        Map<String, String> map = apiMap();

        // 修改swagger中内容
        swaggerHandler(swagger, map);

        return jsonSerializer.toJson(swagger);
    }

    private void swaggerHandler(Swagger swagger, Map<String, String> map) {
        // 处理tags
        swagger.getTags().forEach(tag -> {
            String desc = tag.getName();
            String name = map.get(desc);
            tag.setName(name);
            tag.setDescription(desc);
        });

        List<String> opList = Stream.of("get", "head", "post", "put", "delete", "options", "patch")
                .collect(Collectors.toList());
        // 处理paths
        Map<String, Path> paths = swagger.getPaths();
        paths.forEach((s, path) -> {
            opList.forEach(s1 -> {
                tagsHandler(map, path, s1);
            });
        });
    }

    @SneakyThrows
    private void tagsHandler(Map<String, String> map, Path path, String op) {
        Class<? extends Path> aClass = path.getClass();
        Field field = aClass.getDeclaredField(op);
        field.setAccessible(true);
        Operation operation = (Operation) field.get(path);
        if (Objects.isNull(operation)) {
            return;
        }
        List<String> tags = operation.getTags();
        List<String> newTags = new ArrayList<>();
        tags.forEach(s1 -> {
            newTags.add(map.get(s1));
        });
        operation.setTags(newTags);
    }

    private Map<String, String> apiMap() {
        Map<String, String> map = new HashMap<>();
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Api.class);
        beans.forEach((s, o) -> {
            Class<?> aClass = o.getClass();
            Api api = aClass.getAnnotation(Api.class);
            String description = api.tags()[0];
            String name = controllerNameAsGroup(aClass);
            map.put(description, name);
        });
        return map;
    }

    public static String controllerNameAsGroup(Class<?> bean) {
        String simpleName = bean.getSimpleName();
        int endIndex = simpleName.indexOf("$");
        if (endIndex != -1) {
            // 如果类上存在lombok注解，则类有CGLIB生成
            simpleName = simpleName.substring(0, endIndex);
        }
        return splitCamelCase(simpleName, "-")
                .replace("/", "")
                .toLowerCase();
    }

    private String hostName(UriComponents uriComponents) {
        String hostNameOverride = environment.getProperty(
                "springfox.documentation.swagger.v2.host",
                "DEFAULT");
        if ("DEFAULT".equals(hostNameOverride)) {
            String host = uriComponents.getHost();
            int port = uriComponents.getPort();
            if (port > -1) {
                return String.format("%s:%d", host, port);
            }
            return host;
        }
        return hostNameOverride;
    }
}
